require "mptree"

class Neuron < MPTree::MPTreeNode

	@@id_num = 0

	attr_accessor :weight_sum

	def Neuron.ResetID
		@@id_num = 0
	end

	def sigmoid( t )
		1 / (1 + Math.exp( -t ))

	end

	def initialize( init_out_neurons = [] )
		super( @@id_num )
		@@id_num += 1

		# neuron => weight
		@weights = {}
		self.parents {|p| @weights[p] = rand}

		@unfired_neurons = @parents  # Neurons which haven't provided input
	end

	# Overrides the superclasses addParent to also add a weight when
	# the parent is added
	def addParent( parent, weight = rand )
		super parent
		@weights[parent] = weight
	end
	
	# Provide input to the neuron. Add to the total sum.
	def neuron_input( neuron, input )
		raise( "Neuron is not a parent, hierarchy is incorrect" ) if( !@parents.member? neuron )
		@weight_sum += @in_neurons[neuron] * input

		@unfired_neurons.delete neuron
		self.check_fire
	end

	private

	# Check to see if receiver neuron should fire, and
	# fire it if it is ready
	def check_fire
		# Fire if all inputs have been provided
		fire if (@unfired_neurons.size == 0)
	end
		
	# Fire receiver neuron
	def fire
		sig_sum = sigmoid( @weight_sum )

		self.children {|n| n.neuron_input( self, sig_sum ) }
		reset
	end

	# Reset this neuron so it can fire again
	def reset
		@weight_sum = 0
		@unfired_neurons = @parents
	end
end


class In_Neuron < Neuron

	def neuron_input( val )
		@weight_sum = val
		fire
	end

end


class Out_Neuron < Neuron


	def intialize
		super
		@sig_sum = 0
	end

	def out_val
		sig_sum
	end

	private :addChild

	def fire
		@sig_sum = sigmoid( @weight_sum )
		@sig_sum
	end

end
